package com.lagou.edu.listener;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.StrUtil;
import com.lagou.edu.annotation.*;
import com.lagou.edu.config.ApplicationConfig;
import com.lagou.edu.config.XmlConfigBuilder;
import com.lagou.edu.factory.BeanFactory;
import com.lagou.edu.proxy.ProxyFactory;
import org.reflections.Reflections;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ConfigurationBuilder;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;


/**
 * @author fanbo@imoran.net
 * @date 2020/12/26 下午3:23
 */
public class BeanFactoryListener implements ServletContextListener {


    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        System.out.println("contextInitialized...");

        /** 解析applicationConfig.xml，将信息读取到ApplicationConfig类 */
        parseConfigXml();

        /**  扫描@Service、@Repository、@Component注解的类，添加到容器中 */
        scanAnnotationClass(Service.class, Repository.class, Component.class);

        /** 扫描@Autowire与@Qualifier注解，进行依赖注入 */
        doDependencyInjection();

        /**  扫描添加@Transactional的方法，生成事务代理对象 */
        createTransactionProxy();

    }


    /**
     * 解析applicationConfig.xml，将信息读取到ApplicationConfig类
     *
     * @return
     */
    private void parseConfigXml() {
        System.out.println("解析配置文件applicationConfig.xml...");
        XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder();
        xmlConfigBuilder.parseConfig("applicationConfig.xml");
    }

    /**
     * 扫描@Service注解的类，添加到容器中
     *
     * @param annotations
     */
    @SafeVarargs
    private final void scanAnnotationClass(Class<? extends Annotation>... annotations) {
        System.out.println("实例化对象到容器...");

        // 注解扫描包路径
        String packages = ApplicationConfig.basePackage;

        List<String> list = StrUtil.split(packages, ',', true, true);
        // 扫描框架自身的类
        list.add("com.lagou.edu");
        Reflections reflections = new Reflections(new ConfigurationBuilder()
                .forPackages(list.toArray(new String[0]))
                .addScanners(new TypeAnnotationsScanner())
        );

        for (Class<? extends Annotation> annotation : annotations) {
            Set<Class<?>> serviceTypesAnnotated = reflections.getTypesAnnotatedWith(annotation);
            for (Class<?> aClass : serviceTypesAnnotated) {
                try {
                    Object instance = aClass.newInstance();
                    System.out.println("实例化注解@" + annotation.getSimpleName() + "对象..." + instance);
                    String value = AnnotationUtil.getAnnotationValue(aClass, annotation);
                    if (value.length() == 0) {
                        /** 如果没有指定value，默认使用类名 */
                        value = aClass.getSimpleName();
                    }

                    BeanFactory.map.put(value, instance);
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("容器中的对象：");
        BeanFactory.map.entrySet().forEach(System.out::println);
        System.out.println();
    }

    /**
     * 扫描添加@Transactional的方法，生成事务代理对象
     *
     * @return
     */
    private void createTransactionProxy() {
        System.out.println("生成代理对象...");

        for (Map.Entry<String, Object> entry : BeanFactory.map.entrySet()) {
            String key = entry.getKey();
            Object object = entry.getValue();

            Class<?> aClass = object.getClass();
            Method[] methods = aClass.getDeclaredMethods();
            List<Method> transactionMethods = new ArrayList<>();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Transactional.class)) {
                    transactionMethods.add(method);
                }
            }
            /** 如果所有方法都不包含@Transactional注解，则不生成代理 */
            if (transactionMethods.size() == 0) {
                continue;
            }

            /** 根据该对象是否有接口，选择使用jdk还是cglib代理方式 */
            Class<?>[] interfaces = aClass.getInterfaces();
            ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");

            Object proxyObject;
            if (interfaces.length == 0) {
                proxyObject = proxyFactory.getCglibProxy(object, transactionMethods);
            } else {
                proxyObject = proxyFactory.getJdkProxy(object, interfaces, transactionMethods);
            }
            System.out.println("生成代理对象：" + proxyObject);
            BeanFactory.map.put(key, proxyObject);
        }

    }


    /**
     * 扫描@Autowire与@Qualifier注解，进行依赖注入
     */
    private void doDependencyInjection() {
        System.out.println("依赖注入...");

        /** 遍历容器中的所有对象，根据注解标签扫描是否有字段需要注入 */
        for (Object instance : BeanFactory.map.values()) {
            Field[] fields = instance.getClass().getDeclaredFields();
            for (Field field : fields) {
                /** 处理Autowired注解 */
                if (field.isAnnotationPresent(Autowired.class)) {
                    Class<?> type = field.getType();
                    System.out.println("带有Autowired注解的字段, 字段名为：" + field.getName() + "，类型是：" + type);

                    /** 获取指定类型的所有对象 */
                    List<Map.Entry<String, Object>> entryList = new ArrayList<>();
                    for (Map.Entry<String, Object> entry : BeanFactory.map.entrySet()) {
                        Object value = entry.getValue();
                        Class<?> valueClass = value.getClass();
                        List<Class<?>> valueInterfaceClass = Arrays.asList(value.getClass().getInterfaces());
                        Class<?> valueSupperClass = value.getClass().getSuperclass();
                        if (valueClass.equals(type) || valueInterfaceClass.contains(type) || valueSupperClass.equals(type)) {
                            entryList.add(entry);
                        }
                    }

                    if (entryList.isEmpty()) {
                        throw new RuntimeException(type + "not found");
                    }


                    /** 处理Qualifier注解指定实现类 */
                    Object filedValue = null;
                    if (entryList.size() > 1) {
                        if (field.isAnnotationPresent(Qualifier.class)) {
                            String name = field.getAnnotation(Qualifier.class).value();
                            for (Map.Entry<String, Object> entry : entryList) {
                                if (entry.getKey().equals(name)) {
                                    filedValue = entry.getValue();
                                }
                            }
                            if (filedValue == null) {
                                throw new RuntimeException("容器中未找到名称为" + name + "的实现类");
                            }
                        } else {
                            throw new RuntimeException(type + "发现不止一个，需要使用@Qualifier指定具体使用哪一个");
                        }
                    }
                    /** 如果指定类型的对象只有一个，就使用它注入 */
                    Map.Entry<String, Object> objectEntry = entryList.get(0);
                    filedValue = objectEntry.getValue();
                    field.setAccessible(true);
                    try {
                        /** 反射设置字段的实现类 */
                        field.set(instance, filedValue);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    System.out.println("字段\"" + field.getName() + "\"注入成功");
                }
            }
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        System.out.println("contextDestroyed!!!");
    }
}
